#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <cblas.h>

#define NUM_THREADS 10

typedef struct {
    int thread_id;
    int m, n, k;
    double *a;
    int lda;
    double *b;
    int ldb;
    double *c;
    int ldc;
} thread_data_t;

void *thread_func(void *arg) {
    thread_data_t *data = (thread_data_t *)arg;
    int m_per_thread = data->m / NUM_THREADS;
    int start_row = data->thread_id * m_per_thread;
    int end_row = (data->thread_id == NUM_THREADS - 1) ? data->m : start_row + m_per_thread;

    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
                end_row - start_row, data->n, data->k,
                1.0, &data->a[start_row * data->lda], data->lda,
                data->b, data->ldb,
                1.0, &data->c[start_row * data->ldc], data->ldc);

    pthread_exit(NULL);
}

double get_time() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + tv.tv_usec * 1e-6;
}

int main(int argc, char *argv[]) {
    if (argc != 4) {
        printf("Usage: %s <M> <N> <K>\n", argv[0]);
        return -1;
    }

    int M = atoi(argv[1]);
    int N = atoi(argv[2]);
    int K = atoi(argv[3]);

    pthread_t threads[NUM_THREADS];
    thread_data_t thread_data[NUM_THREADS];
    int i, rc;

    double *a = (double *)malloc(M * K * sizeof(double));
    double *b = (double *)malloc(K * N * sizeof(double));
    double *c = (double *)malloc(M * N * sizeof(double));

    for (i = 0; i < M * K; i++) a[i] = 1.0;
    for (i = 0; i < K * N; i++) b[i] = 1.0;
    for (i = 0; i < M * N; i++) c[i] = 0.0;

    double start_time = get_time();

    for (i = 0; i < NUM_THREADS; i++) {
        thread_data[i].thread_id = i;
        thread_data[i].m = M;
        thread_data[i].n = N;
        thread_data[i].k = K;
        thread_data[i].a = a;
        thread_data[i].lda = K;
        thread_data[i].b = b;
        thread_data[i].ldb = N;
        thread_data[i].c = c;
        thread_data[i].ldc = N;

        rc = pthread_create(&threads[i], NULL, thread_func, (void *)&thread_data[i]);
        if (rc) {
            printf("Error: Unable to create thread, %d\n", rc);
            exit(-1);
        }
    }

    for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }

    double end_time = get_time();
    double elapsed_time = end_time - start_time;
    double gflops = (2.0 * M * N * K * 1e-9) / elapsed_time;

    printf("Time: %f seconds\n", elapsed_time);
    printf("GFLOPS: %f\n", gflops);

    free(a);
    free(b);
    free(c);

    return 0;
}